package auctionsimulation.agent;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.net.UnknownHostException;
import java.util.HashMap;

import auctionsimulation.communication.command.NewAuctionCommand;
import auctionsimulation.communication.command.OfferNewPriceOnAuctionCommand;
import auctionsimulation.communication.response.OKResponse;
import auctionsimulation.helper.AuctionHelper;
import auctionsimulation.market.DoubleAuction;
import auctionsimulation.market.DutchAuction;
import auctionsimulation.market.ReverseEnglishAuction;

public class SellAgent extends Agent {

	private static final long serialVersionUID = 7729925820730019443L;

	private Socket sock = null;
	private ObjectInputStream commandInput = null;
	private ObjectOutputStream commandOutput = null;
	private String sellAuctionType;
	private double price;
	private int amount;
	private long reverseAuctionWaitTime;
	private long dutchAuctionDecreaseInterval;
	private long dutchAuctionDecreaseAmount;
	private long dutchAuctionMinimumPrice;
	private long doubleAuctionRuntime;
	private HashMap<String, Long> productTemplate;

	public SellAgent(String configFileName) {
		super(configFileName);
	}

	@Override
	public void run() {
		this.init();
		try {
			this.createOrBidForAuctions();
			commandOutput.close();
			commandInput.close();
			sock.close();
		} catch (IOException e) {
			System.err.println("Error in SellAgent: ");
			e.printStackTrace();
		}

	}

	private void init() {
		String host = this.behavierProperties.getProperty("host");
		int port = Integer
				.parseInt(this.behavierProperties.getProperty("port"));
		if (host == null)
			throw new PropertyNotFoundOrInvalidException(
					"Error creating agent: property host not found");
		try {
			sock = new Socket(host, port);
		} catch (UnknownHostException e) {
			AuctionHelper.error("host " + host + " with port " + port
					+ "not found", this, e);
		} catch (IOException e) {
			AuctionHelper.error("was not able to open socket", this, e);
		}
		try {
			commandOutput = new ObjectOutputStream(sock.getOutputStream());
		} catch (IOException e) {
			AuctionHelper.error("was not able to open ObjectOutputStream",
					this, e);
		}
		try {
			commandInput = new ObjectInputStream(sock.getInputStream());
		} catch (IOException e) {
			AuctionHelper.error("was not able to open ObjectInputStream", this,
					e);
		}

		try {
			this.price = Double.parseDouble(this.behavierProperties
					.getProperty("price"));
		} catch (NumberFormatException | NullPointerException e) {
			throw new PropertyNotFoundOrInvalidException(
					"Error creating agent: price property not found or invalid in resource file "
							+ this.configFilePath);
		}

		try {
			this.sellAuctionType = this.behavierProperties
					.getProperty("sellauctiontype");
			if (!(this.sellAuctionType.equals("reverse")
					|| this.sellAuctionType.equals("dutch") || this.sellAuctionType
						.equals("double"))) {
				throw new NullPointerException();
			}
		} catch (NullPointerException e) {
			throw new PropertyNotFoundOrInvalidException(
					"Error creating agent: sellauctiontype property not found or invalid in resource file "
							+ this.configFilePath);
		}

		if (this.sellAuctionType.equals("reverse")) {
			try {
				this.reverseAuctionWaitTime = Long
						.parseLong(this.behavierProperties
								.getProperty("reverseauctionwaittime"));
			} catch (NumberFormatException | NullPointerException e) {
				throw new PropertyNotFoundOrInvalidException(
						"Error creating agent: reverseauctionwaittime property not found or invalid in resource file "
								+ this.configFilePath);
			}
		} else if (this.sellAuctionType.equals("dutch")) {
			try {
				this.dutchAuctionDecreaseInterval = Long
						.parseLong(this.behavierProperties
								.getProperty("dutchauctiondecreaseinterval"));
			} catch (NumberFormatException | NullPointerException e) {
				throw new PropertyNotFoundOrInvalidException(
						"Error creating agent: dutchauctiondecreaseinterval property not found or invalid in resource file "
								+ this.configFilePath);
			}
			try {
				this.dutchAuctionDecreaseAmount = Long
						.parseLong(this.behavierProperties
								.getProperty("dutchauctiondecreaseamount"));
			} catch (NumberFormatException | NullPointerException e) {
				throw new PropertyNotFoundOrInvalidException(
						"Error creating agent: dutchauctiondecreaseamount property not found or invalid in resource file "
								+ this.configFilePath);
			}
			try {
				this.dutchAuctionMinimumPrice = Long
						.parseLong(this.behavierProperties
								.getProperty("dutchauctionminimumprice"));
			} catch (NumberFormatException | NullPointerException e) {
				throw new PropertyNotFoundOrInvalidException(
						"Error creating agent: dutchauctionminimumprice property not found or invalid in resource file "
								+ this.configFilePath);
			}
		} else if (this.sellAuctionType.equals("double")) {
			try {
				this.doubleAuctionRuntime = Long
						.parseLong(this.behavierProperties
								.getProperty("doubleauctionruntime"));
			} catch (NumberFormatException | NullPointerException e) {
				throw new PropertyNotFoundOrInvalidException(
						"Error creating agent: doubleauctionruntime property not found or invalid in resource file "
								+ this.configFilePath);
			}
		}

		this.productTemplate = new HashMap<String, Long>();
		try {
			String temp = this.behavierProperties.getProperty("template");
			String[] tempprops = temp.split(",");
			for (String property : tempprops) {
				this.productTemplate.put(property.split(":")[0],
						Long.parseLong(property.split(":")[1]));
			}
		} catch (Exception e) {
			throw new PropertyNotFoundOrInvalidException(
					"Error creating agent: template property not found or invalid in resource file "
							+ this.configFilePath);
		}

		try {
			this.amount = Integer.parseInt(this.behavierProperties
					.getProperty("amount"));
		} catch (NumberFormatException | NullPointerException e) {
			throw new PropertyNotFoundOrInvalidException(
					"Error creating agent: amount property not found or invalid in resource file "
							+ this.configFilePath);
		}
	}

	private boolean createOrBidForAuctions() {
		while (this.amount > 0) {
			try {
				switch (this.sellAuctionType) {
				case "reverse":
					OfferNewPriceOnAuctionCommand bidreverse = new OfferNewPriceOnAuctionCommand(
							"reverse", this, this.price, this.productTemplate);
					commandOutput.writeObject(bidreverse);
					if (!(commandInput.readObject() instanceof OKResponse)) {
						NewAuctionCommand newreverse = new NewAuctionCommand(
								new ReverseEnglishAuction(this,
										this.productTemplate, this.price,
										this.reverseAuctionWaitTime));
						commandOutput.writeObject(newreverse);
						if (!(commandInput.readObject() instanceof OKResponse)) {
							return false;
						} else {
							this.amount--;
						}
					} else {
						this.amount--;
						System.out
								.println("Successfully overbidden other agent!");
					}
					break;
				case "dutch":
					NewAuctionCommand newdutch = new NewAuctionCommand(
							new DutchAuction(this, this.productTemplate,
									this.price,
									this.dutchAuctionDecreaseInterval,
									this.dutchAuctionDecreaseAmount,
									this.dutchAuctionMinimumPrice));

					commandOutput.writeObject(newdutch);
					if (!(commandInput.readObject() instanceof OKResponse)) {
						return false;
					} else {
						this.amount--;
					}
					break;
				case "double":
					OfferNewPriceOnAuctionCommand biddouble = new OfferNewPriceOnAuctionCommand(
							"double", this, this.price, this.productTemplate);
					commandOutput.writeObject(biddouble);
					if (!(commandInput.readObject() instanceof OKResponse)) {
						NewAuctionCommand newdouble = new NewAuctionCommand(
								new DoubleAuction(this, this.productTemplate,
										this.price, this.doubleAuctionRuntime));
						commandOutput.writeObject(newdouble);
						if (!(commandInput.readObject() instanceof OKResponse)) {
							return false;
						} else {
							this.amount--;
						}
					} else {
						System.out
								.println("Successfully overbidden other agent!");
						this.amount--;
					}
					break;
				default:
					throw new IllegalArgumentException(
							"Error in sellagent: property sellauctiontype was not reverse, dutch or double in property file "
									+ this.configFilePath);
				}
			} catch (IOException e) {
				AuctionHelper.error(
						"was not able to send or recieve objects on streams",
						this, e);
				return false;
			} catch (ClassNotFoundException e) {
				AuctionHelper.error("received classtype was invalid", this, e);
				return false;
			}

		}
		return true;
	}

	private class ObjectInputStream extends java.io.ObjectInputStream implements
			Serializable {

		private static final long serialVersionUID = -3085583655867046032L;

		protected ObjectInputStream(InputStream in) throws IOException,
				SecurityException {
			super(in);
		}
	}

	private class ObjectOutputStream extends java.io.ObjectOutputStream
			implements Serializable {

		private static final long serialVersionUID = -7162264508073357801L;

		protected ObjectOutputStream(OutputStream out) throws IOException,
				SecurityException {
			super(out);
		}

	}

	private class Socket extends java.net.Socket implements Serializable {

		private static final long serialVersionUID = 9206444983034513030L;

		public Socket(String host, int port) throws UnknownHostException,
				IOException {
			super(host, port);
		}

	}

}
